﻿using System.Configuration;
using log4net;
using Reactor.Configuration;
using Reactor.Exceptions;
using Reactor.Providers;
using Reactor.Resources;
using Reactor.ServiceGrid.Repositories;
using Samurai.Wakizashi.Workflow;

namespace Reactor.Environment.Workflows
{
    /// <summary>
    /// Workflow responsible for obtaining all necessary information needed by a 
    /// <seealso cref="PlatformConnectionData"/> instance.
    /// </summary>
    public class DefaultEnvironmentBootstrapper : SequentialWorkflow<InitializationContext>
    {
        #region Fields

        private static readonly ILog Log = LogManager.GetLogger(typeof (DefaultEnvironmentBootstrapper));
        private const string NoValueFoundMessage = "Could not locate a value for configuration key: {0}";

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultEnvironmentBootstrapper"/> class.
        /// </summary>
        public DefaultEnvironmentBootstrapper()
        {
            Context = new InitializationContext();

            RegisterStep(AssertThatServiceLocatorInstanceIsPresent);
            RegisterStep(DiscoverDatabaseConnectionString);
            RegisterStep(DefineConfigurationStore);
            RegisterStep(DefineCoreDataProvider);
            RegisterStep(DiscoverBrokerConnectionString);
            RegisterStep(DiscoverServicePackageRepositoryLocation);
        }

        /// <summary>
        /// Asserts that a service locator instance is present by checking the environment context. Throws an exception 
        /// and fails the workflow if not.
        /// </summary>
        public void AssertThatServiceLocatorInstanceIsPresent()
        {
            if(Environment.Context.ServiceLocator == null)
                throw new FatalException(CommonResources.Platform_NoServiceLocator);
        }

        /// <summary>
        /// Discovers the database connection string by looking to application configuration for it.
        /// </summary>
        public void DiscoverDatabaseConnectionString()
        {
            var connString = ConfigurationManager.ConnectionStrings[CommonResources.ConnectionStringName];
            if(connString == null)
                throw new FatalException(CommonResources.Fatal_NoDatabaseConnectionStringFound);

            Context.DatabaseConnectionString = connString.ConnectionString;
        }

        /// <summary>
        /// Defines the configuration aggregator by creating a <seealso cref="ConfigurationAggregator"/> and providing 
        /// it with an instance of a <seealso cref="SqlConfigurationStore"/> and <seealso cref="AppConfigStore"/>. 
        /// The <seealso cref="IConfigurationAggregator"/> instance is then registered with the service locator.
        /// </summary>
        public void DefineConfigurationStore()
        {
            var aggregator = new ConfigurationAggregator();
            Context.ConfigurationStore = new AppConfigStore();

            var configurationDataProvider = new SqlConfigurationDataProvider(Context.DatabaseConnectionString);
            aggregator.RegisterConfigurationStore(new SqlConfigurationStore(configurationDataProvider));
            aggregator.RegisterConfigurationStore(Context.ConfigurationStore);

            Environment.Context.ServiceLocator.RegisterInstance<IConfigurationAggregator>(aggregator);
            Environment.Context.Configuration = aggregator;
        }

        public void DefineCoreDataProvider()
        {
            Environment.Context.ServiceLocator.RegisterType<ICoreDataProvider, SqlCoreDataProvider>();
        }

        /// <summary>
        /// Attempts to discover the broker connection string.
        /// </summary>
        public void DiscoverBrokerConnectionString()
        {
            var brokerConnectionString = Context.ConfigurationStore.GetConfigurationValue(CommonResources.ConfigKey_BrokerConnectionString);

            if(!string.IsNullOrEmpty(brokerConnectionString))
            {
                if(Log.IsInfoEnabled) Log.InfoFormat("Broker connection string found: {0}", brokerConnectionString);
                Context.BrokerDetails = new BrokerConnectionDetails(brokerConnectionString);
            } 
            else
                Log.InfoFormat(NoValueFoundMessage, CommonResources.ConfigKey_BrokerConnectionString);
        }

        /// <summary>
        /// Attempts to discover the service package repository location.
        /// </summary>
        public void DiscoverServicePackageRepositoryLocation()
        {
            var repoLocation = Environment.Context.Configuration.GetConfigurationValue(CommonResources.ConfigKey_PackageRepositoryLocation);

            if (!string.IsNullOrEmpty(repoLocation))
                Context.PackageRepositoryLocation = repoLocation;
            else
                Log.WarnFormat(NoValueFoundMessage, CommonResources.ConfigKey_PackageRepositoryLocation);
        }
    }
}
